/*--- formatted by Jindent 2.1, (www.c-lab.de/~jindent) ---*/

/**
 * ***************************************************************
 * The LEAP libraries, when combined with certain JADE platform components,
 * provide a run-time environment for enabling FIPA agents to execute on
 * lightweight devices running Java. LEAP and JADE teams have jointly
 * designed the API for ease of integration and hence to take advantage
 * of these dual developments and extensions so that users only see
 * one development platform and a
 * single homogeneous set of APIs. Enabling deployment to a wide range of
 * devices whilst still having access to the full development
 * environment and functionalities that JADE provides.
 * Copyright (C) 2001 Telecom Italia LAB S.p.A.
 * Copyright (C) 2001 Broadcom Eireann Research.
 * Copyright (C) 2001 Motorola.
 * 
 * GNU Lesser General Public License
 * 
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation,
 * version 2.1 of the License.
 * 
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 * 
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the
 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
 * Boston, MA  02111-1307, USA.
 * **************************************************************
 */
package jade.imtp.leap.JICP;

//#MIDP_EXCLUDE_FILE

import jade.core.Profile;
import jade.core.ProfileException;
import jade.mtp.TransportAddress;
import jade.imtp.leap.*;
import java.io.*;
import java.net.*;

/**
 * Class declaration
 * @author Giovanni Caire - TILAB
 * @author Jamie Lawrence - Broadcom Eireann Research
 * @author Ronnie Taib - Motorola
 * @author Nicolas Lhuillier - Motorola
 */
public class JICPPeer implements ICP, ProtocolManager {
	private static final int POOL_SIZE = 50;
	
  private JICPClient   client;
  private JICPServer   server;
  private Ticker       ticker;

  private String myID;
  
  private int connectionTimeout = 0;
  
  public static final String CONNECTION_TIMEOUT = "jade_imtp_leap_JICP_JICPPeer_connectiontimeout";
  /**
   * Start listening for internal platform messages on the specified port
   */
  public TransportAddress activate(ICP.Listener l, String peerID, Profile p) throws ICPException {
    myID = peerID;
    
    connectionTimeout = Integer.parseInt(p.getParameter(CONNECTION_TIMEOUT, "0"));
    
  	// Start the client
  	client = new JICPClient(getProtocol(), getConnectionFactory(), POOL_SIZE);
  			
    // Start the server listening for connections
    server = new JICPServer(p, this, l, getConnectionFactory(), POOL_SIZE);
    server.start();

    // Start the Ticker
    ticker = new Ticker(60000);
    ticker.start();
    
    // Creates the local transport address
    TransportAddress localTA = getProtocol().buildAddress(server.getLocalHost(), String.valueOf(server.getLocalPort()), null, null);

    return localTA;
  } 

  /**
   * stop listening for internal platform messages
   */
  public void deactivate() throws ICPException {
  	if (server != null) {
  		client.shutdown();
      server.shutdown();
      ticker.shutdown();
    } 
    else {
      throw new ICPException("No external listener was activated.");
    } 
  } 

  /**
   * deliver a serialized command to a given transport address
   */
  public byte[] deliverCommand(TransportAddress ta, byte[] payload, boolean requireFreshConnection) throws ICPException {
    byte[] respPayload = client.send(ta, JICPProtocol.COMMAND_TYPE, payload, requireFreshConnection);

    return (respPayload);
  } 

  final String getID() {
  	return myID;
  }
  
  /**
   * Pings the specified transport address in order to obtain
   * the local hostname or IP address.
   * @param pingURL The <code>URL</code> to ping (usually the
   * main container).
   * @return The local IP address of the local container as a
   * <code>String</code>.
   * 
   * @throws ICPException
   */
  String getAddress(String pingURL) throws ICPException {
    byte[] respPayload = null;

    try {
      TransportAddress pingAddr = getProtocol().stringToAddr(pingURL);

      respPayload = client.send(pingAddr, JICPProtocol.GET_ADDRESS_TYPE, new byte[0], false);
    } 
    catch (ICPException icpe) {
      throw new ICPException("JICP GET_ADDRESS error. Cannot retrieve local hostname: "
                                 +icpe.getMessage());
    } 

    return (new String(respPayload));
  } 

  /**
     Subclasses may re-define this method to return their own
     protocol
   */
  public TransportProtocol getProtocol() {
    return JICPProtocol.getInstance();
  } 
  
  /**
     Subclasses may re-define this method to return their own
     ConnectionFactory
   */
  public ConnectionFactory getConnectionFactory() {
    return new ConnectionFactory() {
			public Connection createConnection(Socket s) {
				return new JICPConnection(s);
			}
			public Connection createConnection(TransportAddress ta) throws IOException {
				JICPConnection con = new JICPConnection(ta, connectionTimeout);
				return con;
			}
    };
  }  
  
  protected ServerSocket getServerSocket(String host, int port, boolean changePortIfBusy) throws ICPException {
		try {
			return new ServerSocket(port, 50, (host != null ? InetAddress.getByName(host) : null));
		} 
		catch (BindException be) {
			if (changePortIfBusy) {
				// The specified port is busy. Let the system find a free one
				try {
					return new ServerSocket(0, 50, (host != null ? InetAddress.getByName(host) : null));
				} catch (IOException ioe) {
					throw new ICPException("Cannot create server socket on a free port. ", ioe);
				}
			} else {
				throw new ICPException("Cannot bind server socket to "+(host != null ? "host "+host : "localhost")+ " port " + port);
			}
		} catch (IOException ioe2) {
			throw new ICPException("Cannot create server socket. ", ioe2);
		}
	}
  
  /**
     Inner class Ticker
   */
  private class Ticker extends Thread {
  	private long period;
  	private boolean active = false;
  	
  	private Ticker(long period) {
  		super();
  		this.period = period;
  	}
  	
  	public void start() {
  		active = true;
  		super.start();
  	}
  	
  	public void run() {
  		while (active) {
  			try {
  				Thread.sleep(period);
  				long currentTime = System.currentTimeMillis();
  				client.tick(currentTime);
  				server.tick(currentTime);
  			}
  			catch (InterruptedException ie) {
  			}
  		}
  	}
  	
  	public void shutdown() {
  		active = false;
  		interrupt();
  	}
  } // END of inner class Ticker
}

